チュートリアル データは、[Help] メニュー → [Download Tutorials and Examples…] を選択し、[CityEngine Tutorial] からダウンロードできます。
CGA シェープグラマーは、CityEngine の中心をなす技術です。この機能を知ることは自分で建物や都市を構築する上で重要です。シンプルな建物の作成で、基本的なシェープ グラマーを学習します。
演習 |
---|
・Part 1 :シンプルな建物のモデリング |
・Part 2 :シンプルな建物へのテクスチャ貼り付け |
・Part 3 :LOD の追加 |
・Part 4 :建物属性のランダム変化 |
このセクションでは CityEngine の CGA シェープ グラマーの基本事項を学習します。基本的な建物を構築するための一通りのステップを含む完成したルール ファイルの中身を確認していきます。
プロジェクト Tutorial_06_Basic_Shape_Grammar を自分の CityEngine ワークスペースにインポートします。
シーン Tutorial_06_Basic_Shape_Grammar/scenes/01_SimpleBuilding.cej を開きます。
ここでは、典型的な壁面を持ったシンプルな建物を構築します。Part 1 の最終成果物は以下のようになります。
建物の属性情報は (ルール ファイル中のどこでも配置できますが) 通常はルール ファイルの最初の段階で定義されます。これらの属性はルール セット全体で使用され、[CGA Rule Editor] ウィンドウだけでなく、[Inspector] ウィンドウでも値を設定したり編集したりすることができます。
attr groundfloor_height = 4
attr floor_height = 3.5
attr tile_width = 3
attr height = 11
attr wallColor = "#fefefe"
シンプルな建物の構築に使用される窓のアセットはここで定義されています。アセットは [Navigator] ウィンドウの中のプロジェクトの assets フォルダーから読み込まれます。
// geometries
window_asset = "facades/window.obj"
これでもう建物の構築ができる状態です。最初のルールは Lot となっているはずです。[Inspector] ウィンドウで適用されている スタートルールを覚えておいてください。Extrude オペレーションを使用して立体モデルが作成されます。
Lot -->
extrude(height) Building
通常このステップでは、Component 分割を適用することで立体モデルを壁面 (Facade) に分解します。
Building -->
comp(f){ front : FrontFacade | side : SideFacade | top: Roof}
このルールでは、component 分割を適用することにより Building という名前の立体モデルの図形を面に分解します。これにより、前面 (玄関のある側の壁面) の図形、いくつかの側面の図形、屋根 (Roof) の図形ができます。
その後で壁面がモデリングされます。典型的な壁面モデリングのワークフローは次のようになっています。最初のステップで壁面はフロア (Floors) に分けられます。続いてフロアはさらに分解されて、タイル (Tiles)と呼ばれる要素に分けられます。1 つのタイルは壁と窓の要素で構成されています。この分割は CGA シェープグラマーの中で以下のように記述されています。
FrontFacade ルールは、建物の前面を高さ 4 の一階のフロアの図形と、高さ 3.5 の 2 階以上をフロアの繰り返した (繰り返すためにはリピート オペレーター [*] を使用します) 図形に分割します。チルダ オペレーター [~] を使用すると、ビルの実際の高さにかかわらず、すべての上階のフロアが綺麗に配置されるように自動調節されます。特に前面については、1 階フロアの見た目は他のフロアと異なることが多くなります。例えば、1 階には玄関あり、2 階以上のフロアと比べると窓の様子や色なども異なります。
FrontFacade -->
split(y){ groundfloor_height : Groundfloor | { ~floor_height: Floor }* }
SideFacade ルールは、建物の側面をフロアに分割します。したがって、フロアの高さが前面と等しくなるように subdivision 分割が同様に実行されます。
SideFacade -->
split(y){ groundfloor_height: Floor | { ~floor_height: Floor }* }
Floor ルールは、フロアを幅およそ 3 のタイルに分割する典型的な例です。フロアのデザインをもう少し見栄え良くするために、壁要素の両端を幅 1 だけ分割します。
Floor -->
split(x){ 1: Wall
| { ~tile_width: Tile }*
| 1 : Wall }
Groundfloor ルールは、地上フロアのシェープを、右側に玄関が付いた以外は同様の subdivision 分割で整えます。下図は、押し出しにより形成された立体モデル(左)とそれをフロアおよびタイルに分割した状態を示しています。
Groundfloor -->
split(x){ 1: Wall
|{ ~tile_width: Tile }*
| ~tilewidth: EntranceTile
| 1: Wall }
初期的な前面の構造を定義したら、次はタイルをモデリングします。
Tile -->
split(x){ ~1 : Wall
| 2 : split(y){ 1: Wall | 1.5: Window | ~1: Wall }
| ~1 : Wall }
Tile ルールは、x および y 軸方向に (入れ子状の split により) タイルを分割することによりタイルの見栄えを定義します。このデザインでは壁の要素は浮動的 (チルダが付いている) であり、また窓のサイズは幅 2、高さ 1.5 に固定されています。
EntranceTile ルールは玄関のシェープをタイルの場合と同様に定義します (ただし底辺には壁は作りません)。
EntranceTile -->
split(x){ ~1 : SolidWall
| 2 : split(y){ 2.5: Door | ~2: SolidWall }
| ~1 : SolidWall }
最後のルールは、窓や扉や壁シェープの形状を対応するアセットで置き換え、適切に配置し、テクスチャをセットします。
Window -->
s('1,'1,0.4)
t(0,0,-0.25)
i(window_asset)
Door -->
s('1,'1,0.1)
t(0,0,-0.5)
i("builtin:cube")
Wall -->
color(wallColor)
SolidWall -->
color(wallColor)
s('1,'1,0.4)
t(0,0,-0.4)
i("builtin:cube:notex")
オペレーション t(x,y,z) は現在のシェープの位置を z 軸方向に-0.25 移動する、ということになります。これにより、窓とテクスチャは壁面に 0.25m めり込んだ状態になります。この後、Insert オペレーション i(オブジェクト名) により現在のスコープにアセットが挿入されます。Window や Door ルールのようにディメンションが設定されていない場合は、自動的にサイズが適用されます。ディメンションが与えられている場合はそれが使用されます。オペレーション s(x,y,z) により、スコープのサイズが Wall ルールに設定されます。相対座標が使用されているため、スコープの幅や高さは影響を受けません。現在のスコープの x および y 方向は 1 倍にスケール (‘1) されますので結果的に何も変わりません。z 方向は -0.4 に設定されているため、結果的に 0.4m の厚さ(内側向き)を持った壁になります。
ここまでのルールを全て適用すると、テクスチャなしのシンプルな建物が完成します。
Part 2 では、このシンプルな建物モデルにテクスチャを貼り付ける方法を学習します。
このセクションでは、シンプル建物の窓や壁にどのようにテクスチャを貼り付けるかを学習します。
アセットと同様に、使用するテクスチャをルール ファイルの先頭で定義します。テクスチャは assets フォルダーから読み込まれます。
// textures
frontdoor_tex = "facades/textures/shopdoor.tif"
wall_tex = "facades/textures/brickwall.jpg"
dirt_tex = "facades/textures/dirtmap.15.tif"
roof_tex = "roofs/roof.tif"
assets/facade フォルダーには 9 つの異なる窓のテクスチャが用意されています。9 つのテクスチャすべてを単一のアセットとしてリストする代わりに、次の関数が 9 つの窓テクスチャのうちの 1 つを返します。
randomWindowTexture = fileRandom("*facades/textures/window.*.tif")
Frontfacade -->
setupProjection(0, scope.xy, 1.5, 1, 0, 0, 1)
setupProjection(2, scope.xy, scope.sx, scope.sy)
split(y){ groundfloor_height : Groundfloor | { ~floor_height: Floor }* }
Sidefacade -->
setupProjection(0, scope.xy, 1.5, 1, 0, 0, 1)
setupProjection(2, scope.xy, scope.sx, scope.sy)
split(y){ groundfloor_height: Floor | { ~floor_height: Floor }* }
setupProjection() コマンドは、スコープの xy 平面に投影された色 (チャンネル0) とダートマップ (チャンネル1)を建物前面上の UV 座標へ投影する準備をします。したがって scope.xy が第2 パラメーターとしてセットされています。ブロックのテクスチャ (チャンネル0) は X 方向に 1.5m ごと、Y 方向に 1m ごとに繰り返されます。一方、ダートマップ (チャンネル2) は建物前面全体にわたっており、そのために scope.sx および scope.sy がサイズ パラメーターとして使用されています。
Roof -->
setupProjection(0, scope.xy, scope.sx, scope.sy)
texture(roof_tex)
projectUV(0)
Roof ルールは屋根の面全体に適用される UV 座標を準備し、屋根のテクスチャをセットし、ジオメトリにテクスチャ座標を適用します。
Window -->
s('1,'1,0.4)
t(0,0,-0.25)
texture(randomWindowTexture)
i(window_asset)
Door -->
s('1,'1,0.1)
t(0,0,-0.5)
texture(frontdoor_tex)
i("builtin:cube")
窓(Window)と扉(Door)の要素に対しては要求されるテクスチャにカラーマップのみをセットします。窓については getWindowTex() 関数をランダムなインデックスとともに使用し、9 つの窓テクスチャのうちの 1 つを取得します。
Wall -->
color(wallColor)
texture(wall_tex)
set(material.dirtmap, dirt_tex)
projectUV(0) projectUV(2)
SolidWall -->
color(wallColor)
s('1,'1,0.4)
t(0,0,-0.4)
texture(wall_tex)
set(material.dirtmap, dirt_tex)
i("builtin:cube:notex")
projectUV(0) projectUV(2)
下図は壁とアセットにテクスチャを貼り込んだ最終形のモデルです。
同じモデルを拡大した状態:
任意の立体モデルにこのルールセットを適用:
Part 3 では LOD(Level Of Detail、詳細レベル)を追加します。
このセクションでは、ここまでに作成したシンプルな建物に対して簡単な LOD(詳細度)を追加します。モデルの複雑さ(ポリゴン数)を省略することができるため、シンプルな建物をより広範囲で作成する場合に有効です。
新しい属性 LOD を CGA ファイルの既存の属性に追加します。
attr LOD = 1
この例では、2 つの詳細度のみを定義します。
ここまでに作成したシンプル建物は、高解像度モデルになります。これからいくつかのステップにより低解像度バージョンを作成します。
再び現在のモデルを見てみると、窓 (Window) アセットのポリゴンを省略できそうです。複雑な窓アセットの代わりにテクスチャを貼った平面を使用します。
Window -->
case LOD > 0 :
s('1,'1,0.4)
t(0,0,-0.25)
texture(randomWindowTexture)
i(window_asset)
else :
setupProjection(0,scope.xy,scope.sx,scope.sy)
texture(randomWindowTexture)
projectUV(0)
ここでは Window ルールに条件を追加しました。LOD 値が 0 よりも大きければ (つまり高解像度モデルなら)、元々ある高解像度アセットを使用します。そうでない場合 (LOD が 0) は、窓アセットは読み込まずに建物前面に由来する単純な平面を使用します。
Door -->
case LOD > 0 :
s('1,'1,0.1)
t(0,0,-0.5)
texture(frontdoor_tex)
i("builtin:cube")
else :
setupProjection(0,scope.xy,scope.sx,scope.sy)
texture(frontdoor_tex)
projectUV(0)
SolidWall -->
case LOD > 0 :
color(wallColor)
s('1,'1,0.4)
t(0,0,-0.4)
texture(wall_tex)
set(material.dirtmap, dirt_tex)
i("builtin:cube:notex")
projectUV(0) projectUV(2)
else :
Wall
ソースフィールドが [ルール] から [値] に変わります。これは、次世代の建物では、ルールファイルの LOD 値がインスペクターの値 0 によって設定されることを意味します。
次のスクリーンショットは、LOD = 0 のインスペクターの新しい LOD 属性を示しています。
モデルを Wireframe on shaded (7 キーを押す) および Untextured (5 キー) で表示すると、両者の違いが明確になります。ポリゴン数を確認するために D キーを押してヘッドアップ ディスプレイを表示します。モデルを構成するポリゴンの数が 3699 から 475 に減少したことがわかります。
Part 4 ではシンプル建物にランダムな変化を追加します。
このセクションでは、ランダム属性を定義することにより建物の生成に変化をつける方法を学習します。
attr tile_width = rand(2.5, 6)
attr height = rand(8, 35)
attr wallColor = 33% : "#ffffff"
33% : "#999999"
else : "#444444"
attr LOD = 0